/*
 * parser.c -- part of ZilUtils/ZilAsm
 *
 * Copyright (C) 2016 Jason Self <j@jxself.org>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
 *
 * SPDX-License-Identifier: AGPL-3.0-or-later
 */

#include <stdio.h>   /* fopen, fgets */
#include <string.h>  /* strlen */

#include "parser.h"
#include "directives.h"
#include "opcodes.h"
#include "labels.h"

#define iscomment(c)   ((c) == '#')
#define isbindigit(c)  ((c) == '0' || (c) == '1')

/* !!! TODO !!! */
#define fatal_error(errmsg)
#define PC NULL

void checksep(const char *p)
{
	if (!*p || iscomment(*p) || isspace(*p)) return;
	fatal_error("wrong chars");
}

const char *pass_spaces(const char *p)
{
	while(p && isspace(*p)) p++;
	return (p && *p) ? p : NULL;
}

const char *pass_alnums(const char *p)
{
	while(p && isalnum(*p)) p++;
	return (p && *p) ? p : NULL;
}

int tryparse_directive(const char *p)
{
	if (*p != '.')
		return 0;
	const char *a = p+1;
	const char *b = pass_alnums(a);
	checksep(b);
	Directive_handler f = directive_lookup(a, b - a);
	if (!f) return 0;
	return (*f)(b);
}

int tryparse_assignment(const char *a, const char *b, const char *c)
{
	return 0;
}

int tryparse_label(const char *a, const char *b, const char *c)
{
	if (*(c+1) != ':') {
		symtable_add2(Local_labels, a, b - a, PC);
	} else if (*(c+2) != ':') {
		symtable_add2(Global_labels, a, b - a, PC);
	} else {
		fatal_error("wrong label type");
	}

	while (*c++ == ':');
	if (*c && ((c = pass_spaces(c)) != NULL) && *c)
		return tryparse_instruction(c);
	return 1;
}

int tryparse_name(const char *a)
{
	const char *b = pass_alnums(a);
	const char *c = pass_spaces(b);

	if (!c)        return 0;
	if (*c == '=') return tryparse_assignment(a, b, c + 1);
	if (*c == ':') return tryparse_label(a, b, c);
	return 0;
}

int tryparse_instruction(const char *a)
{
	const char *b = pass_alnums(a);
	ZOpcode *op = symtable_lookup2(Opcodes, a, b - a);
	if (!op) return 0;
	ZOpcode_flags flags = op->flags;
	/* !!! TODO !!! */
	return 0;
}

/*
 *  Line can be one from: Comment, Global label, Local label, Directive, Name=Value, Instruction
 */
int parse_line(const char *p)
{
	for (; *p; p++) {
		char c = *p;
		int n;
		if (isspace(c))                  continue;
		if (iscomment(c))                return 0;
		if (n = tryparse_directive(p))   return n;
		if (n = tryparse_name(p))        return n;  // ..label or assignment
		if (n = tryparse_instruction(p)) return n;
		fatal_error("wrong line");
	}
	return 0;
}

int parse_file(const char *filename)
{
	FILE *fp = fopen(filename, "r");
	if (!fp) fatal_error("wrong file");

	const int MAX_LINESIZE = 1024;
	char line[MAX_LINESIZE];
	int newline_missing = 0;

	while (fgets(line, MAX_LINESIZE, fp)) {
		if (newline_missing) fatal_error("line too long");

		int n = strlen(line);
		if (!n) continue;

		parse_line(line);

		newline_missing = (line[n-1] != '\n');
	}

	close(fp);
}

/*

line_passed() {
    skip_spaces();
    return (current_token == LINE_END || current_token == LINE_COMMENT);
}

if (line_passed()) continue;
if (current_token == DIRECTIVE) {
    if (!try_next_token(NAME))
        fatal_error("directive contains incorrect chars")
    handler = get_directive_handler(current_token);
    if (!handler)
        fatal error("unknown directive");
    (*handler)(remaining_line);
    if (line_passed()) continue;
    fatal_error("unexpected line tail");
} else if (current_token == NAME) {
    skip_spaces();
    if (current_token == ASSIGNMENT)
}
    

*/
